﻿using System;
using System.Windows.Graphics;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Resources;

namespace Microsoft.Xna.Framework
{
    /// <summary>
    /// 	A game component that is notified when it needs to draw itself.
    /// </summary>
    public class DrawableGameComponent : GameComponent, IDrawable
    {
        #region Fields & Properties

        private GraphicsDeviceManager _graphicsDeviceManager;

        /// <summary>
        /// 	The GraphicsDevice the DrawableGameComponent is associated with.
        /// </summary>
        public GraphicsDevice GraphicsDevice
        {
            get
            {
                if (_graphicsDeviceManager == null)
                {
                    throw new InvalidOperationException(Resx.PropertyCannotBeCalledBeforeInitialize);
                }
                return _graphicsDeviceManager.GraphicsDevice;
            }
        }

        #endregion

        #region Constructors

        /// <summary>
        /// 	Initializes a new instance of this class.
        /// </summary>
        /// <param name = "game">Game that the game component should be attached to.</param>
        public DrawableGameComponent(Game game) : base(game)
        {
            _visible = true;
        }

        #endregion

        #region Overrides of GameComponent

        #region IGameComponent Methods

        private bool _initialized;

        /// <summary>
        /// 	Initializes the component. 
        /// 	Override this method to load any non-graphics resources and query for any required services.
        /// </summary>
        public override void Initialize()
        {
            base.Initialize();
            if (!_initialized)
            {
                _graphicsDeviceManager = GraphicsDeviceManager.Current;
                if (_graphicsDeviceManager == null)
                {
                    throw new InvalidOperationException(Resx.MissingGraphicsDeviceService);
                }
                _graphicsDeviceManager.RenderModeChanged += HandleGraphicsDeviceManagerRenderModeChanged;
                if (_graphicsDeviceManager.GraphicsDevice != null)
                {
                    LoadContent();
                }
            }
            _initialized = true;
        }

        #endregion

        #region IDisposable Methods

        /// <summary>
        /// 	Releases the unmanaged resources used by the GameComponent and optionally releases the managed resources.
        /// </summary>
        /// <param name = "disposing">true to release both managed and unmanaged resources; false to release only unmanaged resources.</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                UnloadContent();
                if (_graphicsDeviceManager != null)
                {
                    _graphicsDeviceManager.RenderModeChanged -= HandleGraphicsDeviceManagerRenderModeChanged;
                }
            }
            base.Dispose(disposing);
        }

        #endregion

        #endregion

        #region Implementation of IDrawable

        #region Visible

        private bool _visible;

        /// <summary>
        /// 	Indicates whether IDrawable.Draw should be called in Game.Draw for this game component.
        /// </summary>
        public bool Visible
        {
            get
            {
                return _visible;
            }
            set
            {
                if (_visible != value)
                {
                    _visible = value;
                    OnVisibleChanged(EventArgs.Empty);
                }
            }
        }

        public event EventHandler<EventArgs> VisibleChanged;

        /// <summary>
        /// 	Called when the Visible property changes. Raises the VisibleChanged event.
        /// </summary>
        /// <param name = "args">Arguments to the VisibleChanged event.</param>
        protected virtual void OnVisibleChanged(EventArgs args)
        {
            var onVisibleChanged = VisibleChanged;
            if (onVisibleChanged != null)
            {
                onVisibleChanged(this, args);
            }
        }

        #endregion

        #region DrawOrder

        private int _drawOrder;

        /// <summary>
        /// 	Order in which the component should be drawn, relative to other components that are in the same GameComponentCollection.  Reference page contains code sample.
        /// </summary>
        public int DrawOrder
        {
            get
            {
                return _drawOrder;
            }
            set
            {
                if (_drawOrder != value)
                {
                    _drawOrder = value;
                    OnDrawOrderChanged(EventArgs.Empty);
                }
            }
        }

        public event EventHandler<EventArgs> DrawOrderChanged;

        /// <summary>
        /// 	Called when the DrawOrder property changes. Raises the DrawOrderChanged event.
        /// </summary>
        /// <param name = "args">Arguments to the DrawOrderChanged event.</param>
        protected virtual void OnDrawOrderChanged(EventArgs args)
        {
            var onDrawOrderChanged = DrawOrderChanged;
            if (onDrawOrderChanged != null)
            {
                onDrawOrderChanged(this, args);
            }
        }

        #endregion

        /// <summary>
        /// 	Called when the DrawableGameComponent needs to be drawn. 
        /// 	Override this method with component-specific drawing code. Reference page contains links to related conceptual articles.
        /// </summary>
        /// <param name = "gameTime">Time passed since the last call to Draw.</param>
        public virtual void Draw(GameTime gameTime)
        {
        }

        #endregion

        #region Load & Unload Content

        /// <summary>
        /// 	Called when graphics resources need to be loaded. 
        /// 	Override this method to load any component-specific graphics resources.
        /// </summary>
        protected virtual void LoadContent()
        {
        }

        /// <summary>
        /// 	Called when graphics resources need to be unloaded. 
        /// 	Override this method to unload any component-specific graphics resources.
        /// </summary>
        protected virtual void UnloadContent()
        {
        }

        #endregion

        #region Event Handlers

        private void HandleGraphicsDeviceManagerRenderModeChanged(object sender, RenderModeChangedEventArgs e)
        {
            if (e.OldRenderMode == RenderMode.Unavailable)
            {
                LoadContent();
            }
            if (e.RenderMode == RenderMode.Unavailable)
            {
                UnloadContent();
            }
        }

        #endregion
    }
}